import Component from '@ember/component';
import { IHealthScore } from 'wherehows-web/typings/api/datasets/health';
import { computed, setProperties, getProperties, get } from '@ember/object';
import ComputedProperty from '@ember/object/computed';
import { IDropDownOption } from 'wherehows-web/typings/app/dataset-compliance';
import { noop } from 'wherehows-web/utils/helpers/functions';
import { capitalize } from '@ember/string';
import { HealthDataFields } from 'wherehows-web/components/datasets/containers/dataset-health';

/**
 * Adds properties specifically to help the table render each row to the basic health score
 */
interface IRenderedHealthScore extends IHealthScore {
  highlightClass: string;
  isHidden: boolean;
}

/**
 * Properties that define a useful header object to be rendered in the template
 */
interface IHealthTableHeader {
  label: string;
  class: string;
  dropdownOptions: Array<IDropDownOption<string>> | undefined;
  initialDropdown: IDropDownOption<string>;
  onDropdownSelect: (type: string, selection: IDropDownOption<string>) => void;
}

/**
 * Defining a loose interface for the dropdowns object calculated for category and severity based on
 * the received table data
 */
interface IHealthTableDropdowns {
  [key: string]: Array<IDropDownOption<string>>;
}

export default class DatasetsHealthScoreTable extends Component {
  /**
   * Sets the class names binded to the html element generated by this component
   * @type {Array<string>}
   */
  classNames = ['nacho-table', 'dataset-health__score-table'];

  /**
   * Sets the tag to be rendered for the html element generated by this component
   * @type {string}
   */
  tagName = 'table';

  /**
   * Passed in table data, mostly raw detailed information about the compliance score details and the
   * breakdown of such.
   * @type {Array<IHealthScore>}
   */
  tableData: Array<IHealthScore>;

  /**
   * Passed in severity filter. This property lives on the dataset-health container component and its
   * modification is triggered by a click in the graphing component
   * @type {string}
   */
  currentSeverityFilter: string;

  /**
   * Passed in category filter. This property lives on the dataset-health container component and its
   * modification is triggered by a click in the graphing component
   * @type {string}
   */
  currentCategoryFilter: string;

  /**
   * Expected headers for the detailed table. It contains the wording for each header as well as
   * additional properties
   * @type {Array<IHealthTableHeader>}
   */
  headers = computed('dropdownOptions', function(this: DatasetsHealthScoreTable): Array<IHealthTableHeader> {
    const { dropdownOptions, onDropdownSelect } = getProperties(this, 'dropdownOptions', 'onDropdownSelect');

    return (Object.keys(HealthDataFields) as Array<keyof typeof HealthDataFields>).map(header => ({
      label: HealthDataFields[header],
      class: `dataset-health__score-table__${header.toLowerCase()}`,
      dropdownOptions: dropdownOptions[header.toLowerCase()],
      initialDropdown: { label: HealthDataFields[header], value: '' },
      onDropdownSelect: onDropdownSelect.bind(this, header)
    }));
  });

  /**
   * Passed in function from the dataset health container that helps us handle when the user clicks on a
   * specific category from the dropdown menu in the table header
   * @param {IDropDownOption} selection - the selected option from the dropdown
   */
  onCategorySelect: (selection: IDropDownOption<string>) => void;

  /**
   * Passed in function from the dataset health container that helps us handle when the user clicks on a
   * specific severity from the dropdown menu on the table header
   * @param {IDropDownOption} selection - the selected option from the dropdown
   */
  onSeveritySelect: (section: IDropDownOption<string>) => void;

  /**
   * Uses the passed in table data to calculate a dropdown list of options available for filtering by category
   * and severity for the table's rows.
   * @type {ComputedProperty<IHealthTableDropdowns>}
   */
  dropdownOptions = computed('tableData', function(this: DatasetsHealthScoreTable): IHealthTableDropdowns {
    const tableData = get(this, 'tableData');
    const includedOptions = new Set();
    // Starting with a default ALL/NO FILTER option
    const categoryOptions: Array<IDropDownOption<string>> = [{ label: HealthDataFields.category, value: '' }];
    const severityOptions: Array<IDropDownOption<string>> = [{ label: HealthDataFields.severity, value: '' }];

    tableData.forEach(row => {
      const { category, severity } = row;

      if (!includedOptions.has(category)) {
        categoryOptions.push({ label: capitalize(category), value: category });
      }

      if (severity && !includedOptions.has(severity)) {
        severityOptions.push({ label: capitalize(severity), value: severity });
      }
      // Ensures no repeats
      includedOptions.add(category);
      includedOptions.add(severity);
    });

    return { category: categoryOptions, severity: severityOptions };
  });

  /**
   * Calculates table data from the passed in information by appending each row with information that helps to
   * style the table.
   * @type {ComputedProperty<Array<IRenderedHealthScore>>}
   */
  renderedTableData: ComputedProperty<Array<IRenderedHealthScore>> = computed(
    'tableData',
    'currentCategoryFilter',
    'currentSeverityFilter',
    function(this: DatasetsHealthScoreTable): Array<IRenderedHealthScore> {
      const { tableData, currentCategoryFilter: categoryFilter, currentSeverityFilter: severityFilter } = getProperties(
        this,
        'tableData',
        'currentCategoryFilter',
        'currentSeverityFilter'
      );

      return tableData.map(healthScore => ({
        ...healthScore,
        isHidden:
          (!!categoryFilter && healthScore.category !== categoryFilter) ||
          (!!severityFilter && healthScore.severity !== severityFilter),
        highlightClass:
          healthScore.score < 100
            ? `dataset-health__score-table__row--${(healthScore.severity || 'normal').toLowerCase()}`
            : 'dataset-health__score-table__row--successful'
      }));
    }
  );

  /**
   * This is attached to each header object so that we can pass into the dropdown option for that header
   * and allow the user to select from it with a handler
   * @param type - lets us know which dropdown called this function
   * @param selection - the actual dropdown option passed in from the child component
   */
  onDropdownSelect(type: string, selection: IDropDownOption<string>) {
    if (type === 'Category') {
      this.onCategorySelect(selection);
    } else if (type === 'Severity') {
      this.onSeveritySelect(selection);
    }
  }

  constructor() {
    super(...arguments);

    setProperties(this, {
      tableData: this.tableData || [],
      currentSeverityFilter: this.currentSeverityFilter || '',
      currentCategoryFilter: this.currentCategoryFilter || '',
      onCategorySelect: this.onCategorySelect || noop,
      onSeveritySelect: this.onSeveritySelect || noop
    });
  }
}
